

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/fluid.png">
  <link rel="icon" href="/img/fluid.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="John Doe">
  <meta name="keywords" content="">
  
    <meta name="description" content="@[toc] 运行环境 Windows 11 JDK8 IDEA 2021 专业版 Visual Studio Code 2022  一、SpringBoot 整合 JWT 案例 1.1 后端部分项目结构： ![在这里插入图片描述](https:&#x2F;&#x2F;img-blog.csdnimg.cn&#x2F;f39a773d54e74092a2f065e33ad0a944.png# &#x3D;50%x) 1.1.1">
<meta property="og:type" content="article">
<meta property="og:title" content="Web开发 身份验证2 _ SpringBoot 整合 JWT _ 前后端分离策略 _ 跨域问题的出现与解决">
<meta property="og:url" content="http://example.com/2022/07/24/Web%E5%BC%80%E5%8F%91%20%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%812%20_%20SpringBoot%20%E6%95%B4%E5%90%88%20JWT%20_%20%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E7%AD%96%E7%95%A5%20_%20%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98%E7%9A%84%E5%87%BA%E7%8E%B0%E4%B8%8E%E8%A7%A3%E5%86%B3/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description" content="@[toc] 运行环境 Windows 11 JDK8 IDEA 2021 专业版 Visual Studio Code 2022  一、SpringBoot 整合 JWT 案例 1.1 后端部分项目结构： ![在这里插入图片描述](https:&#x2F;&#x2F;img-blog.csdnimg.cn&#x2F;f39a773d54e74092a2f065e33ad0a944.png# &#x3D;50%x) 1.1.1">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://img-blog.csdnimg.cn/2d1a019b0b4c4ab9942b0c5fdb8e84db.png">
<meta property="og:image" content="https://img-blog.csdnimg.cn/6469a1e0261c44ed996701e81dc205ec.gif#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/d5bc03965e5a4a79956af2f9dd87e8af.gif#pic_center">
<meta property="article:published_time" content="2022-07-24T11:36:01.000Z">
<meta property="article:modified_time" content="2022-08-22T15:49:49.965Z">
<meta property="article:author" content="John Doe">
<meta property="article:tag" content="JavaWeb">
<meta property="article:tag" content="JWT">
<meta property="article:tag" content="跨域">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://img-blog.csdnimg.cn/2d1a019b0b4c4ab9942b0c5fdb8e84db.png">
  
  
  
  <title>Web开发 身份验证2 _ SpringBoot 整合 JWT _ 前后端分离策略 _ 跨域问题的出现与解决 - Hexo</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"example.com","root":"/","version":"1.9.2","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 6.2.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>尤 Ni&#39;s Blog</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="Web开发 身份验证2 _ SpringBoot 整合 JWT _ 前后端分离策略 _ 跨域问题的出现与解决"></span>
          
        </div>

        
          
  <div class="mt-3">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-author" aria-hidden="true"></i>
        John Doe
      </span>
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2022-07-24 19:36" pubdate>
          2022年7月24日 晚上
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          18k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          147 分钟
        
      </span>
    

    
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">Web开发 身份验证2 _ SpringBoot 整合 JWT _ 前后端分离策略 _ 跨域问题的出现与解决</h1>
            
              <p class="note note-info">
                
                  
                    本文最后更新于：1 小时前
                  
                
              </p>
            
            
              <div class="markdown-body">
                
                <p>@[toc]</p>
<h1 id="运行环境"><a href="#运行环境" class="headerlink" title="运行环境"></a>运行环境</h1><ul>
<li>Windows 11</li>
<li>JDK8</li>
<li>IDEA 2021 专业版</li>
<li>Visual Studio Code 2022</li>
</ul>
<h1 id="一、SpringBoot-整合-JWT-案例"><a href="#一、SpringBoot-整合-JWT-案例" class="headerlink" title="一、SpringBoot 整合 JWT 案例"></a>一、SpringBoot 整合 JWT 案例</h1><hr>
<h2 id="1-1-后端部分"><a href="#1-1-后端部分" class="headerlink" title="1.1 后端部分"></a>1.1 后端部分</h2><p>项目结构：</p>
<p>![在这里插入图片描述](<a target="_blank" rel="noopener" href="https://img-blog.csdnimg.cn/f39a773d54e74092a2f065e33ad0a944.png#">https://img-blog.csdnimg.cn/f39a773d54e74092a2f065e33ad0a944.png#</a> &#x3D;50%x)</p>
<h3 id="1-1-1-引入-Maven-依赖"><a href="#1-1-1-引入-Maven-依赖" class="headerlink" title="1.1.1 引入 Maven 依赖"></a>1.1.1 引入 Maven 依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-meta">&lt;?xml version=<span class="hljs-string">&quot;1.0&quot;</span> encoding=<span class="hljs-string">&quot;UTF-8&quot;</span>?&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span><br><span class="hljs-tag">         <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.7.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.bbs.uni<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>uni<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>uni<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>Demo project for Spring Boot<span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br><br>        <span class="hljs-comment">&lt;!-- https://mvnrepository.com/artifact/com.auth0/java-jwt --&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.auth0<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>java-jwt<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.19.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.projectlombok<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>lombok<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.18.24<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>8.0.25<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span><br><br><span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>其中关于 JWT 的依赖为 ：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- https://mvnrepository.com/artifact/com.auth0/java-jwt --&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.auth0<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>java-jwt<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.19.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>
<h3 id="1-1-2-定义实体类"><a href="#1-1-2-定义实体类" class="headerlink" title="1.1.2 定义实体类"></a>1.1.2 定义实体类</h3><p>枚举类 HttpStatusEnum 用于标识响应的状态码以及消息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.enums;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">HttpStatusEnum</span> &#123;<br>    SUCCESS(<span class="hljs-number">200</span>, <span class="hljs-string">&quot;请求成功!&quot;</span>),<br>    FAIL(<span class="hljs-number">500</span>, <span class="hljs-string">&quot;服务器内部出错.&quot;</span>),<br>    NOT_FOUND(<span class="hljs-number">404</span>, <span class="hljs-string">&quot;找不到所指定的资源.&quot;</span>),<br>    NOT_AUTH(<span class="hljs-number">401</span>, <span class="hljs-string">&quot;权限不足.&quot;</span>);<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> code;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String msg;<br>    <span class="hljs-keyword">private</span> <span class="hljs-title function_">HttpStatusEnum</span><span class="hljs-params">(<span class="hljs-type">int</span> code, String msg)</span>&#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>        <span class="hljs-built_in">this</span>.msg = msg;<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">code</span><span class="hljs-params">()</span>&#123; <span class="hljs-keyword">return</span> code; &#125;<br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">msg</span><span class="hljs-params">()</span> &#123; <span class="hljs-keyword">return</span> msg; &#125;<br>&#125;<br></code></pre></td></tr></table></figure>
<p><strong>1）R.java</strong> 用于响应前端请求，关于返回数据的请求都是直接返回 R 对象，R 即 Response响应的缩写</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.entity;<br><br><span class="hljs-keyword">import</span> com.bbs.uni.enums.HttpStatusEnum;<br><span class="hljs-keyword">import</span> lombok.Data;<br><br><span class="hljs-keyword">import</span> java.io.Serializable;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 封装响应对象</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> &lt;T&gt;</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">R</span>&lt;T&gt; <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br><br>    <span class="hljs-keyword">private</span> Integer code;  <span class="hljs-comment">// 响应的代码</span><br>    <span class="hljs-keyword">private</span> String msg; <span class="hljs-comment">// 响应的信息</span><br>    <span class="hljs-keyword">private</span> T data;     <span class="hljs-comment">// 响应的数据</span><br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">R</span><span class="hljs-params">()</span>&#123;<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">R</span><span class="hljs-params">(<span class="hljs-type">int</span> code, String message, T data)</span>&#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>        <span class="hljs-built_in">this</span>.setMsg(message);<br>        <span class="hljs-built_in">this</span>.data = data;<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">R</span><span class="hljs-params">(<span class="hljs-type">int</span> code, T data)</span>&#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>        <span class="hljs-built_in">this</span>.data = data;<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">R</span><span class="hljs-params">(<span class="hljs-type">int</span> code, String message)</span>&#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>        <span class="hljs-built_in">this</span>.setMsg(message);<br>    &#125;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 响应成功</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> data 响应的数据</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span>  返回 对象</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> &lt;T&gt; 对象数据的类型</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">success</span><span class="hljs-params">(T data)</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(<span class="hljs-number">200</span>, <span class="hljs-literal">null</span>, data);<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">success</span><span class="hljs-params">(String msg)</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(HttpStatusEnum.SUCCESS.code(), msg);<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">success</span><span class="hljs-params">(String msg, T data)</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(HttpStatusEnum.SUCCESS.code(), msg, data);<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">success</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(HttpStatusEnum.SUCCESS.code(), <span class="hljs-string">&quot;&quot;</span>);<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 响应失败</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> msg 返回的消息</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 返回对象</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> &lt;T&gt; 标记的对象的类型 这里可省略</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">fail</span><span class="hljs-params">(String msg)</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(HttpStatusEnum.FAIL.code(), msg, <span class="hljs-literal">null</span>);<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">fail</span><span class="hljs-params">(<span class="hljs-type">int</span> code, String msg)</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(code, msg, <span class="hljs-literal">null</span>);<br>    &#125;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">fail</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(HttpStatusEnum.FAIL.code(), <span class="hljs-string">&quot;&quot;</span>, <span class="hljs-literal">null</span>);<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 自定义状态的响应</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>&lt;T&gt; R&lt;T&gt; <span class="hljs-title function_">custom</span><span class="hljs-params">(<span class="hljs-type">int</span> code, String msg, T data)</span>&#123;<br>        <span class="hljs-keyword">return</span>  <span class="hljs-keyword">new</span> <span class="hljs-title class_">R</span>&lt;T&gt;(code, msg, data);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><strong>2）User.java</strong> 用户实体类，在进行登录时方便直接封装用户对象<br>这里注意 User.java 必须实现 Serializable 接口，用于声明该类是可序列化的，在解码 JWT 中的 Payload 时，若包含User信息，那么解码时候 User 必须是可系列化的，否则就会报错。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.entity;<br><br><span class="hljs-keyword">import</span> lombok.AllArgsConstructor;<br><span class="hljs-keyword">import</span> lombok.Data;<br><span class="hljs-keyword">import</span> lombok.NoArgsConstructor;<br><br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br>    <span class="hljs-keyword">private</span> Integer id;<br>    <span class="hljs-keyword">private</span> String username;<br>    <span class="hljs-keyword">private</span> String password;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="1-1-3-定义-JWT-工具类"><a href="#1-1-3-定义-JWT-工具类" class="headerlink" title="1.1.3 定义 JWT 工具类"></a>1.1.3 定义 JWT 工具类</h3><p>这里的工具类和之前一样，暂时不做优化，设置一个有效期仅为10秒的 Token，而且 JWT的签名也比较简单，直接采用固定的字符串密钥。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.utils;<br><br><span class="hljs-keyword">import</span> com.auth0.jwt.JWT;<br><span class="hljs-keyword">import</span> com.auth0.jwt.JWTCreator;<br><span class="hljs-keyword">import</span> com.auth0.jwt.algorithms.Algorithm;<br><span class="hljs-keyword">import</span> com.auth0.jwt.interfaces.DecodedJWT;<br><span class="hljs-keyword">import</span> com.bbs.uni.entity.User;<br><span class="hljs-keyword">import</span> com.fasterxml.jackson.databind.ObjectMapper;<br><span class="hljs-keyword">import</span> lombok.extern.slf4j.Slf4j;<br><span class="hljs-keyword">import</span> org.springframework.util.Base64Utils;<br><br><span class="hljs-keyword">import</span> java.io.IOException;<br><span class="hljs-keyword">import</span> java.util.*;<br><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JWTUtil</span> &#123;<br>    <span class="hljs-comment">// 服务端签名密钥 （不能泄露）</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">TOKEN_SECRET</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;IamUNI&quot;</span>;<br>    <span class="hljs-comment">// 生成 Token</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">createToken</span><span class="hljs-params">(User user)</span>&#123;<br>        <span class="hljs-comment">// 使用 JDK 自带的 java.util.Calendar 日历工具类</span><br>        <span class="hljs-comment">// 获取当前时间</span><br>        <span class="hljs-type">Date</span> <span class="hljs-variable">date</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();<br>        log.info(<span class="hljs-string">&quot;[&#123;&#125;]Creating Token: &quot;</span>, date);<br>        <span class="hljs-type">Calendar</span> <span class="hljs-variable">calendar</span> <span class="hljs-operator">=</span> Calendar.getInstance();<br>        <span class="hljs-comment">// 设置为 10 秒</span><br>        calendar.add(Calendar.SECOND, <span class="hljs-number">10</span>);<br>        <span class="hljs-comment">// 创建构造器</span><br>        JWTCreator.<span class="hljs-type">Builder</span> <span class="hljs-variable">builder</span> <span class="hljs-operator">=</span> JWT.create();<br>        <span class="hljs-comment">// 设置 claim</span><br>        <span class="hljs-keyword">return</span> builder.withClaim(<span class="hljs-string">&quot;uid&quot;</span>, user.getId())<br>                .withClaim(<span class="hljs-string">&quot;username&quot;</span>, user.getUsername())<br>                .withIssuedAt(date) <span class="hljs-comment">// 发行时间</span><br>                .withExpiresAt(calendar.getTime()) <span class="hljs-comment">// 过期时间</span><br>                .sign(Algorithm.HMAC256(TOKEN_SECRET));      <span class="hljs-comment">// 签名</span><br>    &#125;<br>    <span class="hljs-comment">// 解析 Token</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> DecodedJWT <span class="hljs-title function_">verifyToken</span><span class="hljs-params">(String token)</span>&#123;<br>        <span class="hljs-comment">// 采取统一的解码方式</span><br>        <span class="hljs-keyword">return</span> JWT.require(Algorithm.HMAC256(TOKEN_SECRET))<br>                  .build()<br>                  .verify(token);<br>    &#125;<br>    <span class="hljs-comment">// 解析 Payload</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Map&lt;String, Object&gt; <span class="hljs-title function_">decodePayload</span><span class="hljs-params">(String payload)</span>&#123;<br>        <span class="hljs-type">ObjectMapper</span> <span class="hljs-variable">objectMapper</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ObjectMapper</span>();<br>        <span class="hljs-type">HashMap</span> <span class="hljs-variable">hashMap</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br>        <span class="hljs-keyword">try</span> &#123;<br>            hashMap = objectMapper.readValue(Base64Utils.decodeFromString(payload), HashMap.class);<br>        &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>            e.printStackTrace();<br>        &#125;<br>        <span class="hljs-keyword">return</span> hashMap;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>
<h3 id="1-1-4-定义拦截器"><a href="#1-1-4-定义拦截器" class="headerlink" title="1.1.4 定义拦截器"></a>1.1.4 定义拦截器</h3><p>拦截器类 JWTInterceptor.java ，该类相比于之前进行了重写，之前由于是前后端不分离的，所以拦截器里需要考虑重定向的页面，同时也要考虑 @ResponseBody 返回其响应体的情况，而在前后端分离中，Controller 返回结果大部分是响应体数据。</p>
<ul>
<li>若 Token 认证成功，那么其加密的字符串以及解码后 payload 所包含的对象都存储到 Map，放置在 Request 对象中，Controller层的 Handler 方法可以获取到这些数据。</li>
<li>若 Token 认证失败，设置 Reponse 的返回类型为 JSON，然后把错误提示封装给 R 对象，返回给前端。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.interceptor;<br><br><span class="hljs-keyword">import</span> com.auth0.jwt.exceptions.AlgorithmMismatchException;<br><span class="hljs-keyword">import</span> com.auth0.jwt.exceptions.JWTDecodeException;<br><span class="hljs-keyword">import</span> com.auth0.jwt.exceptions.SignatureVerificationException;<br><span class="hljs-keyword">import</span> com.auth0.jwt.exceptions.TokenExpiredException;<br><span class="hljs-keyword">import</span> com.auth0.jwt.interfaces.DecodedJWT;<br><span class="hljs-keyword">import</span> com.bbs.uni.entity.R;<br><span class="hljs-keyword">import</span> com.bbs.uni.enums.HttpStatusEnum;<br><span class="hljs-keyword">import</span> com.bbs.uni.utils.JWTUtil;<br><span class="hljs-keyword">import</span> com.fasterxml.jackson.databind.ObjectMapper;<br><span class="hljs-keyword">import</span> lombok.extern.slf4j.Slf4j;<br><span class="hljs-keyword">import</span> org.springframework.web.servlet.HandlerInterceptor;<br><br><span class="hljs-keyword">import</span> javax.servlet.http.HttpServletRequest;<br><span class="hljs-keyword">import</span> javax.servlet.http.HttpServletResponse;<br><span class="hljs-keyword">import</span> java.util.HashMap;<br><span class="hljs-keyword">import</span> java.util.Map;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 自定义拦截器类</span><br><span class="hljs-comment"> * JWT 拦截器</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JWTInterceptor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">HandlerInterceptor</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">preHandle</span><span class="hljs-params">(</span><br><span class="hljs-params">            HttpServletRequest request,</span><br><span class="hljs-params">            HttpServletResponse response,</span><br><span class="hljs-params">            Object handler)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-comment">// 响应体的数据</span><br>        Map&lt;String,Object&gt; map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>        <span class="hljs-comment">// 响应体对象</span><br>        R&lt;Map&lt;String,Object&gt;&gt; result;<br>        <span class="hljs-comment">// 从请求头中获取 Token</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> request.getHeader(<span class="hljs-string">&quot;token&quot;</span>);<br>        log.info(<span class="hljs-string">&quot;Received Token: &quot;</span> + token);<br>        <span class="hljs-keyword">if</span>(token == <span class="hljs-literal">null</span>) &#123;<br>            log.info(<span class="hljs-string">&quot;VerifyToken: [] Result: Token不存在&quot;</span>);<br>            result = R.fail(<span class="hljs-string">&quot;Token不存在&quot;</span>);<br>        &#125; <span class="hljs-keyword">else</span> &#123;<br>            <span class="hljs-keyword">try</span> &#123;<br>                <span class="hljs-type">DecodedJWT</span> <span class="hljs-variable">decodedJWT</span> <span class="hljs-operator">=</span> JWTUtil.verifyToken(token);<br>                <span class="hljs-comment">// 存 token 和 token内的 payload 有效载荷信息</span><br>                map.put(<span class="hljs-string">&quot;token&quot;</span>, decodedJWT.getToken());<br>                map.put(<span class="hljs-string">&quot;payload&quot;</span>, JWTUtil.decodePayload(decodedJWT.getPayload()));<br>            &#125; <span class="hljs-keyword">catch</span> (SignatureVerificationException e) &#123;<br>                log.error(<span class="hljs-string">&quot;VerifyToken: &quot;</span> + token + <span class="hljs-string">&quot; Result: Token的签名无效&quot;</span>);<br>                result = R.fail(<span class="hljs-string">&quot;token的签名无效&quot;</span>);<br>                e.printStackTrace();<br>            &#125; <span class="hljs-keyword">catch</span> (TokenExpiredException e) &#123;<br>                log.error(<span class="hljs-string">&quot;VerifyToken: &quot;</span> + token + <span class="hljs-string">&quot; Result: Token已过期&quot;</span>);<br>                result = R.fail(<span class="hljs-string">&quot;token已过期&quot;</span>);<br>                e.printStackTrace();<br>            &#125; <span class="hljs-keyword">catch</span> (AlgorithmMismatchException e) &#123;<br>                log.error(<span class="hljs-string">&quot;VerifyToken: &quot;</span> + token + <span class="hljs-string">&quot; Result: Token算法不一致&quot;</span>);<br>                result = R.fail(<span class="hljs-string">&quot;token算法与服务端的不一致&quot;</span>);<br>                e.printStackTrace();<br>            &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>                log.error(<span class="hljs-string">&quot;VerifyToken: &quot;</span> + token + <span class="hljs-string">&quot; Result: Token无效&quot;</span>);<br>                result = R.fail(<span class="hljs-string">&quot;token无效&quot;</span>);<br>                e.printStackTrace();<br>            &#125;<br>        &#125;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ObjectMapper</span>().writeValueAsString(result);<br><br>        <span class="hljs-keyword">if</span>(!result.getCode().equals(HttpStatusEnum.SUCCESS.code()))&#123; <span class="hljs-comment">// 根据响应结果判断是否通行</span><br>            response.setContentType(<span class="hljs-string">&quot;application/json;charset=utf-8&quot;</span>);<br>            response.getWriter().println(json);      <span class="hljs-comment">// 记录响应信息</span><br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>        &#125;<span class="hljs-keyword">else</span>&#123;<br>            request.setAttribute(<span class="hljs-string">&quot;token&quot;</span>, map);<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>        &#125;<br><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li>
</ul>
<h3 id="1-1-5-拦截器配置"><a href="#1-1-5-拦截器配置" class="headerlink" title="1.1.5 拦截器配置"></a>1.1.5 拦截器配置</h3><p>拦截器类 JWTConfig.java，目前只拦截 &#x2F;user 请求，毕竟在登录时，token还没有生成，所以不能执行自定义的 JWT 拦截器，这里只拦截&#x2F;user主要是为了方便测试。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.config;<br><br><span class="hljs-keyword">import</span> com.bbs.uni.interceptor.JWTInterceptor;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;<br><span class="hljs-keyword">import</span> org.springframework.web.servlet.config.annotation.InterceptorRegistry;<br><span class="hljs-keyword">import</span> org.springframework.web.servlet.config.annotation.WebMvcConfigurer;<br><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JWTConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addInterceptors</span><span class="hljs-params">(InterceptorRegistry registry)</span> &#123;<br>        registry.addInterceptor(<span class="hljs-keyword">new</span> <span class="hljs-title class_">JWTInterceptor</span>())<br>                .addPathPatterns(<span class="hljs-string">&quot;/user&quot;</span>);         <span class="hljs-comment">// 拦截 /user</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="1-2-前端部分"><a href="#1-2-前端部分" class="headerlink" title="1.2 前端部分"></a>1.2 前端部分</h2><p>前端比较简单，就一个HTML 页面，使用 Visual Studio Code 编写，部署的话就直接用 VS的拓展插件 Live Server ，项目启动的默认端口号为 5500，使用 JQuery 进行 AJAX 请求</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">&quot;X-UA-Compatible&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;IE=edge&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;width=device-width, initial-scale=1.0&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>测试<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;loginForm&quot;</span> <span class="hljs-attr">method</span>=<span class="hljs-string">&quot;post&quot;</span>&gt;</span><br>        用户名: <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;username&quot;</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br>        密码: <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;password&quot;</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;button&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;btn-login&quot;</span>&gt;</span>登录<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;btn-get&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;button&quot;</span>&gt;</span>获取用户数据<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;result&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text/javascript&quot;</span>&gt;</span><span class="language-javascript"></span><br><span class="language-javascript">        <span class="hljs-keyword">function</span> <span class="hljs-title function_">login</span>(<span class="hljs-params"></span>)&#123;</span><br><span class="language-javascript">            $.<span class="hljs-title function_">ajax</span>(&#123;</span><br><span class="language-javascript">                <span class="hljs-attr">url</span>: <span class="hljs-string">&#x27;http://localhost:8080/user/login&#x27;</span>,</span><br><span class="language-javascript">                <span class="hljs-attr">data</span>: $(<span class="hljs-string">&#x27;#loginForm&#x27;</span>).<span class="hljs-title function_">serialize</span>(),</span><br><span class="language-javascript">                <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;POST&#x27;</span>,</span><br><span class="language-javascript">                <span class="hljs-attr">error</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>)&#123;</span><br><span class="language-javascript">                    <span class="hljs-title function_">alert</span>(<span class="hljs-string">&#x27;请求错误&#x27;</span>)</span><br><span class="language-javascript">                    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(error.<span class="hljs-property">responseText</span>)</span><br><span class="language-javascript">                &#125;,</span><br><span class="language-javascript">                <span class="hljs-attr">success</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)&#123;</span><br><span class="language-javascript">                    <span class="hljs-keyword">if</span>(data.<span class="hljs-property">code</span> === <span class="hljs-number">200</span>)&#123;</span><br><span class="language-javascript">                        <span class="hljs-title function_">alert</span>(<span class="hljs-string">&#x27;登录成功! token已保存在localStorage&#x27;</span>)</span><br><span class="language-javascript">                        <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">setItem</span>(<span class="hljs-string">&quot;token&quot;</span>, data.<span class="hljs-property">data</span>)</span><br><span class="language-javascript">                    &#125; <span class="hljs-keyword">else</span>&#123;</span><br><span class="language-javascript">                        <span class="hljs-title function_">alert</span>(data.<span class="hljs-property">msg</span>)</span><br><span class="language-javascript">                    &#125;</span><br><span class="language-javascript">                    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(data)</span><br><span class="language-javascript">                &#125;</span><br><span class="language-javascript">            &#125;)</span><br><span class="language-javascript">        &#125;</span><br><span class="language-javascript">        <span class="hljs-keyword">function</span> <span class="hljs-title function_">getUserList</span>(<span class="hljs-params"></span>)&#123;</span><br><span class="language-javascript">            $.<span class="hljs-title function_">ajax</span>(&#123;</span><br><span class="language-javascript">                <span class="hljs-attr">url</span>: <span class="hljs-string">&#x27;http://localhost:8080/user&#x27;</span>,</span><br><span class="language-javascript">                <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;GET&#x27;</span>,</span><br><span class="language-javascript">                <span class="hljs-attr">headers</span>: &#123;</span><br><span class="language-javascript">                    <span class="hljs-string">&#x27;token&#x27;</span>:<span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">getItem</span>(<span class="hljs-string">&#x27;token&#x27;</span>),</span><br><span class="language-javascript">                &#125;,</span><br><span class="language-javascript">                <span class="hljs-attr">error</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>)&#123;</span><br><span class="language-javascript">                    <span class="hljs-title function_">alert</span>(<span class="hljs-string">&#x27;请求错误&#x27;</span>)</span><br><span class="language-javascript">                    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(error.<span class="hljs-property">responseText</span>)</span><br><span class="language-javascript">                &#125;,</span><br><span class="language-javascript">                <span class="hljs-attr">success</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)&#123;</span><br><span class="language-javascript">                    <span class="hljs-keyword">if</span>(data.<span class="hljs-property">code</span> === <span class="hljs-number">200</span>)&#123;</span><br><span class="language-javascript">                        <span class="hljs-title function_">alert</span>(<span class="hljs-string">&#x27;查询完毕!&#x27;</span>)</span><br><span class="language-javascript">                        $(<span class="hljs-string">&#x27;#result&#x27;</span>).<span class="hljs-title function_">text</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(data.<span class="hljs-property">data</span>))</span><br><span class="language-javascript">                        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;@&#x27;</span>, data)</span><br><span class="language-javascript">                    &#125; <span class="hljs-keyword">else</span> <span class="hljs-title function_">alert</span>(data.<span class="hljs-property">msg</span>)</span><br><span class="language-javascript">                    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(data)</span><br><span class="language-javascript">                &#125;</span><br><span class="language-javascript">            &#125;)</span><br><span class="language-javascript">        &#125;</span><br><span class="language-javascript">        <span class="hljs-comment">// 绑定按钮监听, 登陆按钮点击后登陆</span></span><br><span class="language-javascript">        $(<span class="hljs-string">&#x27;#btn-login&#x27;</span>).<span class="hljs-title function_">click</span>(<span class="hljs-function">()=&gt;</span>&#123;<span class="hljs-title function_">login</span>()&#125;)</span><br><span class="language-javascript">        $(<span class="hljs-string">&#x27;#btn-get&#x27;</span>).<span class="hljs-title function_">click</span>(<span class="hljs-function">() =&gt;</span>&#123;<span class="hljs-title function_">getUserList</span>()&#125;)</span><br><span class="language-javascript">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure>

<h2 id="1-3-测试【跨域失败】"><a href="#1-3-测试【跨域失败】" class="headerlink" title="1.3 测试【跨域失败】"></a>1.3 测试【跨域失败】</h2><p>后端启动 SpringBoot，默认端口号为 8080<br>前端使用 Visual Studio Code 的 Live Server 部署，默认端口号为 5500</p>
<p><img src="https://img-blog.csdnimg.cn/2d1a019b0b4c4ab9942b0c5fdb8e84db.png" srcset="/img/loading.gif" lazyload alt="在这里插入图片描述"></p>
<p>结果发生报错，具体的报错信息为：</p>
<blockquote>
<p>Access to XMLHttpRequest at ‘<a target="_blank" rel="noopener" href="http://localhost:8080/user/login'">http://localhost:8080/user/login'</a> from origin ‘<a href="http://127.0.0.1:5500&#39;">http://127.0.0.1:5500&#39;</a> has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.</p>
</blockquote>
<p>翻译：</p>
<blockquote>
<p>在’<a target="_blank" rel="noopener" href="http://localhost:8080/user/login'">http://localhost:8080/user/login'</a>从原点’<a target="_blank" rel="noopener" href="http://127.0.0.1:5500/">http://127.0.0.1:5500</a>“”已被CORS策略阻止：请求的资源上不存在“Access Control Allow Origin”标头。</p>
</blockquote>
<p>接下来，我们将先了解一下相关的问题根源。</p>
<h1 id="二、跨域问题与解决概述"><a href="#二、跨域问题与解决概述" class="headerlink" title="二、跨域问题与解决概述"></a>二、跨域问题与解决概述</h1><hr>
<h2 id="2-1-浏览器跨域"><a href="#2-1-浏览器跨域" class="headerlink" title="2.1 浏览器跨域"></a>2.1 浏览器跨域</h2><p> W3C （万维网国际标准化组织）规定了不同网站之间的访问限制，以 JavaScript 为例，通常有两种限制，一是对浏览器的操作权限，二是对远程操作的权限</p>
<p>JavaScript 操作客户端（浏览器）的限制：</p>
<ul>
<li>不允许写本地目录和文件</li>
<li>不允许删除本地目录和文件</li>
</ul>
<p>比如我们在 CSDN网站里 可以通过本地的图片文件上传头像，而不能直接修改或删除本地的头像。</p>
<p>JavaScript 操作其他服务端的限制：</p>
<ul>
<li>JS 脚本代码对远程服务器的操作限制称为 <strong>同源策略</strong>（Same-Origin Policy) 即 JS 脚本只能访问它与包含它的文档在同一域（domain）下的文档。</li>
</ul>
<h2 id="2-2-域"><a href="#2-2-域" class="headerlink" title="2.2 域"></a>2.2 域</h2><p>域，通常使用 domain 来表示，一个网址的组成部分分别是 <code>协议://主机:端口号/路径</code>，同一个域满足其中的 <code>协议://主机:端口</code>这三个部分相同。<br>例如：<code>http://localhost:8080</code>，它的同源和不同源的网址有（这里假定uni映射的IP与localhost映射的IP不同：</p>
<table>
<thead>
<tr>
<th align="left">URL</th>
<th align="left">访问方式</th>
<th align="left">原因</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a target="_blank" rel="noopener" href="http://localhost:8080/user/login">http://localhost:8080/user/login</a></td>
<td align="left">同源</td>
<td align="left">协议、主机和端口号相同</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="http://localhost:8080/index">http://localhost:8080/index</a></td>
<td align="left">同源</td>
<td align="left">协议、主机和端口号相同</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="http://uni:8080/">http://uni:8080</a></td>
<td align="left">跨域</td>
<td align="left">主机名不同</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="http://localhost/">http://localhost:80</a></td>
<td align="left">跨域</td>
<td align="left">端口不同</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://localhost:8080/">https://localhost:8080</a></td>
<td align="left">跨域</td>
<td align="left">协议不同</td>
</tr>
</tbody></table>
<h2 id="2-3-跨域的限制"><a href="#2-3-跨域的限制" class="headerlink" title="2.3 跨域的限制"></a>2.3 跨域的限制</h2><p>在前端页面中，跨域主要会带来三个限制：</p>
<ul>
<li>Cookie、LocalStorage 和 IndexDB 无法共享</li>
<li>DOM元素无法获得</li>
<li>AJAX 请求不能发送</li>
</ul>
<p>这种限制提升了访问网站时双方的安全性。</p>
<h2 id="2-4-解决跨域的措施"><a href="#2-4-解决跨域的措施" class="headerlink" title="2.4 解决跨域的措施"></a>2.4 解决跨域的措施</h2><h3 id="2-4-1-CORS-标准"><a href="#2-4-1-CORS-标准" class="headerlink" title="2.4.1 CORS 标准"></a>2.4.1 CORS 标准</h3><p>CORS 是一个 W3C 标准，全称是跨域资源共享（CORSs-origin resource sharing），它允许浏览器向跨源服务器，发出 XMLHttpRequest 请求。<br>其实，准确的来说，跨域机制是阻止了数据的跨域获取，不是阻止请求发送，CORS需要浏览器和服务器 <strong>同时</strong> 支持。</p>
<p>整个CORS通信过程，都是浏览器自动完成，不需要用户参与。对于开发者来说，CORS通信与同源的AJAX通信没有差别，代码完全一样。</p>
<h3 id="2-4-2-JSONP跨域"><a href="#2-4-2-JSONP跨域" class="headerlink" title="2.4.2 JSONP跨域"></a>2.4.2 JSONP跨域</h3><p>JSONP 即 JSON With Padding（填充式 JSON 的简写）<br>JSONP 实现跨域的原理：动态创建 <script> 标签，利用 script 的 src属性不受同源策略的约束来跨域获取数据。<br>JSONP 分成两部分：回调函数 + 数据，</p>
<ul>
<li>回调函数是当响应来到时，在页面调用的函数，该名称是src请求中指定</li>
<li>数据是传入回调函数中的JSON数据</li>
</ul>
<p>这里不做深入，了解一下即可。</p>
<h1 id="三、SpringBoot-解决跨域"><a href="#三、SpringBoot-解决跨域" class="headerlink" title="三、SpringBoot 解决跨域"></a>三、SpringBoot 解决跨域</h1><hr>
<p>浏览器已支持 CORS 跨域，目前只要服务端支持 CORS 跨域就能成功访问了，接下来我们先通过配置服务端，即 SpringBoot 来实现跨域访问。</p>
<h2 id="3-1-局部跨域"><a href="#3-1-局部跨域" class="headerlink" title="3.1 局部跨域"></a>3.1 局部跨域</h2><p>SpringBoot 支持在某个Controller类上标注@CrossOrigin 来支持CORS 跨域，例如，在之前案例的 UserController 类里加入这个注解：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.controller;<br><br><span class="hljs-keyword">import</span> com.bbs.uni.entity.R;<br><span class="hljs-keyword">import</span> com.bbs.uni.entity.User;<br><span class="hljs-keyword">import</span> com.bbs.uni.utils.JWTUtil;<br><span class="hljs-keyword">import</span> lombok.extern.slf4j.Slf4j;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.CrossOrigin;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.PostMapping;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;<br><br><span class="hljs-keyword">import</span> java.util.ArrayList;<br><span class="hljs-keyword">import</span> java.util.List;<br><br><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@CrossOrigin(origins = &quot;*&quot;, maxAge = 3000)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserController</span> &#123;<br>    <span class="hljs-meta">@PostMapping(&quot;/user/login&quot;)</span><br>    <span class="hljs-keyword">public</span> R&lt;String&gt; <span class="hljs-title function_">login</span><span class="hljs-params">(User user)</span>&#123;<br>        <span class="hljs-keyword">if</span>(<span class="hljs-string">&quot;admin&quot;</span>.equals(user.getUsername())<br>            &amp;&amp; <span class="hljs-string">&quot;123456&quot;</span>.equals(user.getPassword()))&#123;<br>            <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> JWTUtil.createToken(user);<br>            log.info(<span class="hljs-string">&quot;success to verify token: &#123;&#125;&quot;</span>, token);<br>            <span class="hljs-keyword">return</span> R.success(<span class="hljs-string">&quot;登录成功!&quot;</span>, token);<br>        &#125;<br>        <span class="hljs-keyword">return</span> R.fail(<span class="hljs-string">&quot;登陆失败, 用户名或密码错误.&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@GetMapping(&quot;/user&quot;)</span><br>    <span class="hljs-keyword">public</span> R&lt;List&lt;User&gt;&gt; <span class="hljs-title function_">findALl</span><span class="hljs-params">()</span>&#123;<br>        List&lt;User&gt; userList = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();<br>        userList.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>(<span class="hljs-number">1</span>, <span class="hljs-string">&quot;孙悟空&quot;</span>, <span class="hljs-string">&quot;123456&quot;</span>));<br>        userList.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>(<span class="hljs-number">1</span>, <span class="hljs-string">&quot;唐僧&quot;</span>, <span class="hljs-string">&quot;123456&quot;</span>));<br>        <span class="hljs-keyword">return</span> R.success(userList);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>特点：能解决 Controller 的访问跨域，但不能解决拦截器的跨域</p>
<p>测试结果：<br>这里可以跨域登陆，但是不能跨域获取用户数据，因为拦截器默认不支持跨域</p>
<p><img src="https://img-blog.csdnimg.cn/6469a1e0261c44ed996701e81dc205ec.gif#pic_center" srcset="/img/loading.gif" lazyload alt="在这里插入图片描述"></p>
<h2 id="3-2-全局跨域"><a href="#3-2-全局跨域" class="headerlink" title="3.2 全局跨域"></a>3.2 全局跨域</h2><p>参考 <a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1924258">https://cloud.tencent.com/developer/article/1924258</a><br>在 JWT 配置类中添加 addCorsMappings()方法，配置关于跨域的内容</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.bbs.uni.config;<br><br><span class="hljs-keyword">import</span> com.bbs.uni.interceptor.JWTInterceptor;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.CrossOrigin;<br><span class="hljs-keyword">import</span> org.springframework.web.servlet.config.annotation.CorsRegistry;<br><span class="hljs-keyword">import</span> org.springframework.web.servlet.config.annotation.InterceptorRegistry;<br><span class="hljs-keyword">import</span> org.springframework.web.servlet.config.annotation.WebMvcConfigurer;<br><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JWTConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addInterceptors</span><span class="hljs-params">(InterceptorRegistry registry)</span> &#123;<br>        registry.addInterceptor(<span class="hljs-keyword">new</span> <span class="hljs-title class_">JWTInterceptor</span>())<br>                .addPathPatterns(<span class="hljs-string">&quot;/user&quot;</span>);         <span class="hljs-comment">// 拦截 /user</span><br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addCorsMappings</span><span class="hljs-params">(CorsRegistry registry)</span> &#123;<br>        <span class="hljs-comment">// 添加映射路径</span><br>        registry.addMapping(<span class="hljs-string">&quot;/**&quot;</span>)<br>                .allowCredentials(<span class="hljs-literal">true</span>) <span class="hljs-comment">// 允许发送 Cookie</span><br>                 .allowedOriginPatterns(<span class="hljs-string">&quot;*&quot;</span>)    <span class="hljs-comment">// 放行的域, 支持跨域</span><br>                .allowedMethods(<span class="hljs-string">&quot;GET&quot;</span>, <span class="hljs-string">&quot;POST&quot;</span>, <span class="hljs-string">&quot;PUT&quot;</span>, <span class="hljs-string">&quot;DELETE&quot;</span>) <span class="hljs-comment">// 支持的请求</span><br>                .allowedHeaders(<span class="hljs-string">&quot;*&quot;</span>)    <span class="hljs-comment">// 放行的原始请求头信息</span><br>                .exposedHeaders(<span class="hljs-string">&quot;*&quot;</span>);    <span class="hljs-comment">// 暴露原始请求头信息</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>
<p>通过这种方式，我依然没解决拦截器跨域的问题，于是采取第三种，过滤器跨域</p>
<h2 id="3-3-定义过滤器支持跨域【推荐】"><a href="#3-3-定义过滤器支持跨域【推荐】" class="headerlink" title="3.3 定义过滤器支持跨域【推荐】"></a>3.3 定义过滤器支持跨域【推荐】</h2><p>定义过滤器直接注入 Bean 即可，SpringBoot Web 自动配置类会自动扫描到这个 Bean<br>过滤器的执行周期比拦截器早，所以在这配置跨域比较合理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Bean</span><br><span class="hljs-keyword">public</span> CorsFilter <span class="hljs-title function_">corsFilter</span><span class="hljs-params">()</span>&#123;<br>    <span class="hljs-type">CorsConfiguration</span> <span class="hljs-variable">config</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">CorsConfiguration</span>();<br>    config.addAllowedOriginPattern(<span class="hljs-string">&quot;*&quot;</span>);<br>    config.setAllowCredentials(<span class="hljs-literal">true</span>);<br>    config.addAllowedHeader(<span class="hljs-string">&quot;*&quot;</span>);<br>    config.addAllowedMethod(<span class="hljs-string">&quot;*&quot;</span>);<br>    <span class="hljs-type">UrlBasedCorsConfigurationSource</span> <span class="hljs-variable">configSource</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UrlBasedCorsConfigurationSource</span>();<br>    configSource.registerCorsConfiguration(<span class="hljs-string">&quot;/**&quot;</span>, config);<br>    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">CorsFilter</span>(configSource);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>最终测试效果：（完美解决了跨域问题）</p>
<p><img src="https://img-blog.csdnimg.cn/d5bc03965e5a4a79956af2f9dd87e8af.gif#pic_center" srcset="/img/loading.gif" lazyload alt="在这里插入图片描述"></p>
<h1 id="四、总结"><a href="#四、总结" class="headerlink" title="四、总结"></a>四、总结</h1><hr>
<p>通过本次练习，熟悉了前后端分离项目中，JWT 的认证方式，以及后端跨域的设置，其实前端也可以设置跨域，不过个人觉得还是后端去处理跨域比较好，这样一来，前端只需在调接口时只关心 token是否需要传入请求头，而不用担心跨域的问题。本次前端使用的是 JQuery来实现 AJAX 提交请求，在下次学习中，我会尝试使用 Vue2 + SpringBoot 来实现 JWT 认证。</p>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p>[1] <a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1924258">https://cloud.tencent.com/developer/article/1924258</a></p>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/categories/JavaEE/" class="category-chain-item">JavaEE</a>
  
  
    <span>></span>
    
  <a href="/categories/JavaEE/Web/" class="category-chain-item">Web</a>
  
  

  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/tags/JavaWeb/">#JavaWeb</a>
      
        <a href="/tags/JWT/">#JWT</a>
      
        <a href="/tags/%E8%B7%A8%E5%9F%9F/">#跨域</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>Web开发 身份验证2 _ SpringBoot 整合 JWT _ 前后端分离策略 _ 跨域问题的出现与解决</div>
      <div>http://example.com/2022/07/24/Web开发 身份验证2 _ SpringBoot 整合 JWT _ 前后端分离策略 _ 跨域问题的出现与解决/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>John Doe</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2022年7月24日</div>
        </div>
      
      
      <div class="license-meta-item">
        <div>许可协议</div>
        <div>
          
            
            
              <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
              <span class="hint--top hint--rounded" aria-label="BY - 署名">
                <i class="iconfont icon-by"></i>
              </span>
              </a>
            
          
        </div>
      </div>
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2022/07/25/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E7%AC%94%E8%AE%B07%20_%20%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%20%E5%9C%A8%E6%BA%90%E7%A0%81%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8%20Spring%E7%AF%87/" title="设计模式 笔记7 _ 抽象工厂模式 在源码中的应用 Spring篇">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">设计模式 笔记7 _ 抽象工厂模式 在源码中的应用 Spring篇</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2022/07/24/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E7%AC%94%E8%AE%B06%20_%20%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%20%E5%9C%A8%20JDK%20%E6%BA%90%E7%A0%81%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8%20_%20JDBC%20%E7%9F%A5%E8%AF%86%E5%9B%9E%E9%A1%BE%20_%20java.sql%20%E9%87%8C%E7%9A%84%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%20Connection%EF%BC%8C%E5%85%B6%E5%86%85%E9%83%A8%E5%A3%B0%E6%98%8E%E4%BA%86%E4%B8%8D%E5%90%8C%E7%9A%84%20Statement%20%E6%8A%BD%E8%B1%A1%E4%BA%A7%E5%93%81/" title="设计模式 笔记6 _ 抽象工厂模式 在 JDK 源码中的应用 _ JDBC 知识回顾 _ java.sql 里的抽象工厂 Connection，其内部声明了不同的 Statement 抽象产品">
                        <span class="hidden-mobile">设计模式 笔记6 _ 抽象工厂模式 在 JDK 源码中的应用 _ JDBC 知识回顾 _ java.sql 里的抽象工厂 Connection，其内部声明了不同的 Statement 抽象产品</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
    </div>
  
  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      headingSelector : CONFIG.toc.headingSelector || 'h1,h2,h3,h4,h5,h6',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      collapseDepth   : CONFIG.toc.collapseDepth || 0,
      scrollSmooth    : true,
      headingsOffset  : -boardTop
    });
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.10/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/js/local-search.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
